package com.aabte.auth;

import com.aabte.commons.ddd.shared.DomainEvent;

import java.util.LinkedList;
import java.util.List;

public class DomainEventPublisher {

    @SuppressWarnings("unchecked")
    private static final ThreadLocal<List<DomainEventSubscriber>> SUBSCRIBERS = new ThreadLocal<>();

    private static final ThreadLocal<Boolean> PUBLISHING = ThreadLocal.withInitial(() -> Boolean.FALSE);

    private DomainEventPublisher() {
    }

    public static DomainEventPublisher instance() {
        return new DomainEventPublisher();
    }

    @SuppressWarnings("unckecked")
    public <T> void publish(final T domainEvent) {
        if (PUBLISHING.get()) {
            return;
        }

        try {
            PUBLISHING.set(Boolean.TRUE);
            List<DomainEventSubscriber> registeredSubscribers = SUBSCRIBERS.get();

            if (registeredSubscribers != null) {
                Class<?> eventType = domainEvent.getClass();
                for (DomainEventSubscriber subscriber : registeredSubscribers) {
                    Class<?> subscribedTo = subscriber.subscribedToEventType();
                    if (subscribedTo == eventType || subscribedTo == DomainEvent.class) {
                        subscriber.handleEvent(domainEvent);
                    }
                }
            }
        } finally {
            PUBLISHING.set(Boolean.FALSE);
        }
    }

    public DomainEventPublisher reset() {
        if (!PUBLISHING.get()) {
            SUBSCRIBERS.set(null);
        }
        return this;
    }

    @SuppressWarnings("unckecked")
    public <T> void subscribe(DomainEventSubscriber<T> subscriber) {
        if (PUBLISHING.get()) {
            return;
        }

        List<DomainEventSubscriber> registeredSubscribers = SUBSCRIBERS.get();
        if (null == registeredSubscribers) {
            registeredSubscribers = new LinkedList<>();
            SUBSCRIBERS.set(registeredSubscribers);
        }
        registeredSubscribers.add(subscriber);
    }
}
